package com.sk.compiler.processor;


import com.google.auto.service.AutoService;
import com.sk.api.annotations.Mapper;
import com.sk.api.annotations.MapperConfig;
import com.sk.compiler.info.MapperInfo;

import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({"com.sk.api.annotations.Mapper","com.sk.api.annotations.MapperConfig"})
public class MapperProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        MapperInfo info=loadInfo(annotations,roundEnv);
//        processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,info.toString());
        if(info.getPackageName()!=null) {
            try {
                writeClass(info);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    private void writeClass(MapperInfo info) throws IOException {
        JavaFileObject sourceFile = processingEnv.getFiler()
                .createSourceFile(info.getPackageName() + "." + info.getClassName());
        Writer writer = sourceFile.openWriter();
        writer.write("package "+info.getPackageName()+";\n" +
                "\n");
        Set<String> packages = info.getPackages();
        for (String item:packages){
            writer.write("import "+item+";\n");
        }
        writer.write("import com.sk.api.annotations.Bean;\n" +
                "import retrofit2.Retrofit;\n" +
                "\n" +
                "public class "+info.getClassName()+" {\n" +
                "\n");
        for (String item:packages){
            String className = getClassName(item);
            String methodName =lowFirst(className);
            writer.write("    @Bean\n" +
                    "    public "+className+" "+methodName+"(Retrofit retrofit){\n" +
                    "        return retrofit.create("+className+".class);\n" +
                    "    }\n" +
                    "\n");
        }
        writer.write("}");
        writer.close();
    }

    private String lowFirst(String className) {
        return className.substring(0,1).toLowerCase()+className.substring(1);
    }

    private String getClassName(String fullClass) {
        return fullClass.substring(fullClass.lastIndexOf('.')+1);
    }

    private MapperInfo loadInfo(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        MapperInfo mapperInfo=new MapperInfo();
        for (TypeElement annotation:annotations){
            Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotation);
            for (Element element:elements){
                if(element.getAnnotation(Mapper.class)!=null){
                    handleMapper(mapperInfo, (TypeElement) element);
                }else {
                    handleMapperConfig(mapperInfo, (TypeElement) element);
                }
            }
        }
        return mapperInfo;
    }

    private void handleMapperConfig(MapperInfo mapperInfo, TypeElement element) {
        MapperConfig mapperConfig = element.getAnnotation(MapperConfig.class);
        mapperInfo.setPackageName(mapperConfig.packageName());
        mapperInfo.setClassName(mapperConfig.className());
    }

    private void handleMapper(MapperInfo mapperInfo, TypeElement element) {
        String fullClass = element.getQualifiedName().toString();
        mapperInfo.addPackage(fullClass);
    }

}